package com.ants.boot.core.security.jwt;

import com.ants.boot.core.security.user.JwtUser;
import com.ants.boot.core.security.utils.Cons;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @author 蚂蚁会花呗
 * @date 2022/2/14 16:54
 * token 提供类
 */
@Component
public class JwtTokenProvider {

    Log logger = LogFactory.getLog(JwtTokenProvider.class);


    @Resource
    private JwtTokenProperties jwtTokenProperties;



    /**
     * 从令牌中获取数据声明
     *
     * @param token 令牌
     * @return 数据声明
     */
    private Claims getClaimsFromToken(String token) {
        Claims claims;
        try {
            claims = Jwts.parser().setSigningKey(jwtTokenProperties.getTokenSecret()).parseClaimsJws(token).getBody();
        } catch (Exception e) {
            logger.error("get token body error!" + e.getMessage());
            claims = null;
        }
        return claims;
    }

    /**
     * 创建token
     * @param jwtUser
     * @return
     */
    public String createJwtToken(JwtUser jwtUser) {
        //过期时间
        Date expireTime =new Date(System.currentTimeMillis()+ jwtTokenProperties.getTokenExpiredMs());
        //创建 token
        String token = Jwts.builder()
                .claim(Cons.JWT_TOKEN_USERNAME,jwtUser.getUserName())
                .claim(Cons.JWT_TOKEN_USER_ID,jwtUser.getUserId())
                .setExpiration(expireTime)
                .setIssuedAt(new Date())
                .signWith(SignatureAlgorithm.HS512, jwtTokenProperties.getTokenSecret())
                .compact();
        return token;
    }


    /**
     * 从token中获取username
     * @param token
     * @return
     */
    public String getUserNameFromToken(String token){
        String username;
        try {
            Claims claims = getClaimsFromToken(token);
            username = claims.get(Cons.JWT_TOKEN_USERNAME).toString();
        } catch (Exception e) {
            logger.error("get username from token error!" + e.getMessage());
            username = null;
        }
        return username;
    }

    /**
     * 从token中获取userId
     * @param token
     * @return
     */
    public Integer getUserIdFromToken(String token){
        Integer userId;
        try {
            Claims claims = getClaimsFromToken(token);
            userId = Integer.valueOf(claims.get(Cons.JWT_TOKEN_USER_ID).toString());
        } catch (Exception e) {
            logger.error("get username from token error!" + e.getMessage());
            userId = null;
        }
        return userId;
    }

    /**
     * 校验token
     * 可能出现的异常
     * 1.ExpiredJwtException token 过期，这里不做处理，有专门的方法调用
     * 2.UnsupportedJwtException 验证的token和期待的token格式不一样
     * 3.MalformedJwtException 不是一个正确方法创建的token
     * 4.SignatureException token签名验证失败异常
     * 5.IllegalArgumentException token为null或者空异常
     *
     * @param token
     * @return
     */
    public boolean validateToken(String token) {
        try {
            Jwts.parser()
                    .setSigningKey(jwtTokenProperties.getTokenSecret())
                    .parseClaimsJws(token);
            return true;
        } catch (Exception e) {
            logger.error("get username from token error!" + e.getMessage());
            if (e instanceof ExpiredJwtException) {
                return true;
            } else {
                return false;
            }
        }
    }


    /**
     * 判断令牌是否过期
     *
     * @param token 令牌
     * @return 是否过期
     */
    public Boolean isTokenExpired(String token) {
        try {
            Claims claims = getClaimsFromToken(token);
            Date expiration = claims.getExpiration();
            return expiration.after(new Date());
        } catch (Exception e) {
            logger.error("Judge whether the token has expired error！" + e.getMessage());
            return false;
        }
    }

    /**
     * 判断令牌是否过期并刷新令牌
     *
     * @param token 令牌
     * @return 是否过期
     */
    public Map<String,Object> isTokenExpiredAndRefreshToken(String token) {
        Map<String,Object> map = new HashMap<>();
        try {
            Claims claims = getClaimsFromToken(token);
            long expireTime = claims.getExpiration().getTime();
            long currentTime = new Date().getTime();
            if (expireTime > currentTime){
                if ((expireTime - currentTime) <= jwtTokenProperties.getTokenRefreshMs()){
                    map.put("token",refreshToken(token));
                }
                map.put("status",true);
            }else{
                map.put("status",false);
            }
            return map;
        } catch (Exception e) {
            logger.error("Judge whether the token has expired error！" + e.getMessage());
            return new HashMap<>();
        }
    }

    /**
     * 刷新令牌
     *
     * @param token 原令牌
     * @return 新令牌
     */
    public String refreshToken(String token) {
        String refreshedToken;
        try {
            JwtUser jwtUser = new JwtUser(getUserIdFromToken(token),getUserNameFromToken(token));
            refreshedToken = createJwtToken(jwtUser);
        } catch (Exception e) {
            logger.error("refreshedToken failed!" + e.getMessage());
            refreshedToken = null;
        }
        return refreshedToken;
    }

    /**
     * 校验时间, 过期时间与当前时间对比，临近设置过期的话，自动刷新token
     * @param token
     * @return
     */
    public String checkLoginTimeAndRefreshToken(String token){
        String refreshedToken;
        long expireTime = getClaimsFromToken(token).getExpiration().getTime();
        long currentTime = System.currentTimeMillis();
        if (expireTime - currentTime <= jwtTokenProperties.getTokenRefreshMs()) {
            token = refreshToken(token);
        }
        return refreshedToken = token;
    }

    public static void main(String[] args) {
        //过期时间
        Date expireTime =new Date(System.currentTimeMillis()+ 1000);
        //创建 token
        String token = Jwts.builder()
                .claim(Cons.JWT_TOKEN_USERNAME,"18698589620")
                .claim(Cons.JWT_TOKEN_USER_ID,16)
                .setExpiration(expireTime)
                .setIssuedAt(new Date())
                .signWith(SignatureAlgorithm.HS512, "ants")
                .compact();
        System.out.println(token);
    }


}
